package com.tbit.uqbike.service.business.tag.strategy.add;

import cn.hutool.core.collection.CollectionUtil;
import com.tbit.uqbike.enums.TagOperationType;
import com.tbit.uqbike.object.business.UserInfo;
import com.tbit.uqbike.object.pojo.Machine;
import com.tbit.uqbike.object.pojo.tag.MachineTag;
import com.tbit.uqbike.object.pojo.vo.BatchOperationMsgVO;
import com.tbit.uqbike.service.base.MachineService;
import com.tbit.uqbike.service.business.tag.MachineInfoValidationResult;
import com.tbit.uqbike.webmanager.dao.core.MachineDao;
import com.tbit.uqbike.webmanager.dto.TagBindDTO;
import com.tbit.uqbike.webmanager.resp.tag.TagResult;
import com.tbit.uqbike.webmanager.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 通过设备编号 machineNo 添加标签
 *
 *
 * @author <a href="mailto:chen.chaoxiong@tbit.com.cn">ChenChaoXiong</a>
 * @since 2024/7/12
 */
@Component
public class AddByMachineNoStrategy extends AbstractFillInfoOperation{

    @Resource
    private MachineDao machineDao;


    @Override
    protected BatchOperationMsgVO<TagResult> addTag(TagBindDTO tagBind) {
        Integer brandId = tagBind.getBrandId();
        Integer accountId = tagBind.getAccountId();
        UserInfo userInfo = tagBind.getUserInfo();

        Set<String> machineNos = tagBind.getMachineNos();
        Set<Long> tagIds = tagBind.getTagIds();
        if (CollectionUtil.isEmpty(machineNos) || CollectionUtil.isEmpty(tagIds)) {
            return BatchOperationMsgVO.empty();
        }

        BatchOperationMsgVO<TagResult> results = new BatchOperationMsgVO<>();
        MachineInfoValidationResult<String> machineInfoValidationResult = validateMachineNos(machineNos, brandId, accountId, results);

        if (!validateAndFilterTags(tagIds, brandId, accountId, results) || !machineInfoValidationResult.isValid()) {
            return results;
        }

        Map<Integer,String> machineNoMap = machineInfoValidationResult.getMachineinfoMap();
        Set<Integer> machineIds = machineNoMap.keySet();
        List<MachineTag> addableMachineTags = caculateAddableMachineTags(machineIds, tagIds, brandId, accountId, userInfo,results);
        saveMachineTags(addableMachineTags, results);
        fillMachineInfo(results,machineNoMap);
        return results;
    }

    @Override
    protected void fillMachineInfo(BatchOperationMsgVO<TagResult> results, Map<Integer, String> machineNoMap) {
        List<TagResult> allResults = new ArrayList<>();
        allResults.addAll(results.getSuccessList());
        allResults.addAll(results.getFailList());
        allResults.addAll(results.getAbnormalList());

        allResults.forEach(tagResult -> {
            Integer machineId = tagResult.getMachineId();
            if (machineNoMap.containsKey(machineId)) {
                tagResult.setMachineNo(machineNoMap.get(machineId));
            }
        });
    }

    /**
     * 校验设备编号是否存在
     * 将不存在的设备编号放入异常结果集
     * 返回存在的设备编号对应的设备ID集合
     * @param machineNos
     * @param brandId
     * @param accountId
     * @param results
     * @return
     */
    private MachineInfoValidationResult<String> validateMachineNos(Set<String> machineNos, Integer brandId, Integer accountId, BatchOperationMsgVO<TagResult> results) {
        if (CollectionUtil.isEmpty(machineNos)) {
            return MachineInfoValidationResult.invalid();
        }

        Map<String, Object> params = new HashMap<>();
        params.put("accountIds", Collections.singletonList(accountId));
        params.put("machineNOs", StringUtil.getArrayListString(new ArrayList<>(machineNos)));
        List<Machine> machines = machineDao.getByAccountIdAndMachineNOs(params);
        if (CollectionUtil.isEmpty(machines)) {
            results.putAllAbnormalValue(
                    machineNos.stream()
                              .map(machineNo ->
                                      TagResult.builder()
                                               .brandId(brandId)
                                               .accountId(accountId)
                                               .machineNo(machineNo)
                                               .success(false)
                                               .message("Machine does not exist")
                                               .build()
                                      )
                              .collect(Collectors.toList())
            );
            return MachineInfoValidationResult.invalid();
        }

        Set<String> existingMachineNos = machines.stream()
                                                 .map(Machine::getMachineNO)
                                                 .collect(Collectors.toSet());
        Set<String> notExistsMachineNos = machineNos.stream()
                                                    .filter(machineNo -> !existingMachineNos.contains(machineNo))
                                                    .collect(Collectors.toSet());

        if (!notExistsMachineNos.isEmpty()) {
            results.putAllAbnormalValue(
                    notExistsMachineNos.stream()
                                       .map(machineNo ->
                                               TagResult.builder()
                                                        .brandId(brandId)
                                                        .accountId(accountId)
                                                        .machineNo(machineNo)
                                                        .success(false)
                                                        .message("Machine does not exist")
                                                        .build()
                                               )
                                       .collect(Collectors.toList())
            );
        }

        Map<Integer,String> userCodeMap = machines.stream()
                                                  .collect(Collectors.toMap(Machine::getMachineId, Machine::getMachineNO));
        return MachineInfoValidationResult.valid(userCodeMap);
    }

    @Override
    public TagOperationType getOperationType() {
        return TagOperationType.ADD_BY_MACHINE_NO;
    }
}
